package eerbaar.internal.elements;

import java.util.ArrayList;
import java.util.List;

import eerbaar.elements.IElement;
import eerbaar.elements.IElementListener;
import eerbaar.extension.ExtensionPackage;
import eerbaar.extension.IExtensible;
import eerbaar.position.IPosition;
import eerbaar.position.IPositionListener;
import eerbaar.position.PositionFactory;
import eerbaar.position.WrongPositionException;

public class Element implements IElement {

    private String name;
    private IPosition position;
    private final List<IElementListener> listeners = new ArrayList<IElementListener>();

    private final IPositionListener positionListener = new IPositionListener() {

        @Override
        public void afterPositionChange(IPosition oldPos, IPosition newPos) {
            fireAfterMove(oldPos, newPos);
        }

        @Override
        public void beforePositionChange(IPosition pos)
                throws WrongPositionException {
            fireBeforePositionChange(pos);
        }

        @Override
        public String toString() {
            return Element.this.toString();
        }

    };

    private final ExtensionPackage ePackage = new ExtensionPackage();

    public Element(String name, int x, int y) {
        this.name = name;
        setPosition(PositionFactory.createPosition(x, y));
    }

    public Element(String name, IPosition position) {
        this.name = name;
        setPosition(position);
    }

    @Override
    public void addListener(IElementListener listener) {
        if (!this.listeners.contains(listener)) {
            this.listeners.add(listener);
        }
    }

    @Override
    public IElement copy() {

        IElement result = new Element(this.name, this.position.getX(),
                this.position.getY());

        return result;
    }

    private void fireAfterMove(IPosition oldPos, IPosition newPos) {
        List<IElementListener> temp = new ArrayList<IElementListener>();
        temp.addAll(this.listeners);
        for (IElementListener listener : temp) {
            listener.afterMove(oldPos, newPos);
        }
    }

    private void fireBeforePositionChange(IPosition newPos)
            throws WrongPositionException {
        for (IElementListener listener : this.listeners) {
            listener.beforePositionChange(newPos);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IExtensible> T getExtension(Class<T> type) {

        if (type == IElement.class) {
            return (T) this;
        }

        return this.ePackage.getExtension(type);
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public IPosition getPosition() {
        return this.position;
    }

    @Override
    public void installExtension(Class<?> type, IExtensible extension) {
        this.ePackage.installExtension(type, extension);
    }

    @Override
    public void removeListener(IElementListener listener) {
        if (this.listeners.contains(listener)) {
            this.listeners.remove(listener);
        }
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setPosition(final IPosition inPosition) {

        IPosition old = null;
        if (this.position != null) {

            try {
                this.fireBeforePositionChange(this.position);
            } catch (WrongPositionException e) {
                assert (false);
            }

            old = PositionFactory.createPosition(this.position);
        }

        if (this.position != null) {
            this.position.removeListener(this.positionListener);
        }

        inPosition.addListener(this.positionListener);
        this.position = inPosition;

        if (old != null) {
            this.fireAfterMove(old, inPosition);
        }

    }

    @Override
    public String toString() {
        return this.name + "(" + this.position + ")";
    }

    @Override
    public void uninstallExtension(Class<?> type) {
        this.ePackage.uninstallExtension(type);
    }

}
